#include<stdio.h>
#include<arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

int socketFDArr[3000];//用来存放客户端的套接字fd
struct sockaddr_in addrArr[3000];//用来存放客户端的地址的数组
int count = 0;//记录当前有多少用户连接上了

//消息结构体
typedef struct msg
{
     uint32_t ip;
     uint16_t port;
     char buf[1000];//消息的内容
}MSG;

//把字符串IP转为数字IP
uint32_t stringIPtoNum(char * pIP)
{
     uint32_t nIP = 0;
     // AF_INET是代表IPV4地址的意思，AF_INET6代表IPV6
     inet_pton(AF_INET,pIP,&nIP);
     return nIP;
}

//把数字IP转为字符串IP
void numIPtoString(uint32_t ip, char * psIP, int size)
{
     inet_ntop(AF_INET,&ip,psIP,size);
}

//工作线程函数
void * work(void * pData)
{
     //取出创建线程的时候传入的套接字
     int fd = *(int*)pData;
     while(1)
     {
          MSG m1;
          //接受网络发来的消息
          if(0>=read(fd,&m1,sizeof(m1)))
          {
               close(fd);
               break;
          }
          printf("收到消息：%s\n",m1.buf);

          //遍历整个地址数组，把消息转发出去
          for(int i = 0 ; i < count ; i++)
          {
               //把收到的消息发给数组中IP和端口相对应的用户
               if(addrArr[i].sin_addr.s_addr == m1.ip && addrArr[i].sin_port == m1.port)
               {
                    //转发消息
                    write(socketFDArr[i],&m1,sizeof(m1));  
               }
          }
     }
}

int main()
{
     

     //创建一个SOCKET，并且返回一个套接字的文件描述符
     int fd = socket(AF_INET,SOCK_STREAM,0);
     if(fd == -1)
     {
          perror("");
     }

     //地址结构体，需要填写IP，端口，地址族（IPV4/IPV6）
     // typedef struct sockaddr_in MYADDR;
     struct sockaddr_in addr;
     addr.sin_addr.s_addr = stringIPtoNum("192.168.70.37");//指定IP
     addr.sin_port = htons(9001);//指定端口
     addr.sin_family = AF_INET;//指定地址协议族

     //第二个参数因为历史原因，系统设定的是struct sockaddr类型，而现在常用的类型是struct sockaddr_in
     //所以这里要做一个强制转换
     if(-1 == bind(fd,(struct sockaddr*)&addr,sizeof(addr)))
     {
          perror("");
     }
     //启动监听功能，允许5个客户端排队等待
     if(-1==listen(fd,5))
     {
          perror("");
     }

     struct sockaddr_in clientAddr;//用来存放客户端的地址
     socklen_t len = sizeof(clientAddr);
     //使用无限循环一直接收客户端的链接
     while(1)
     {
          pthread_t pt;
          //阻塞等待客户端的链接，如果有客户链接上该函数才会返回，否则会一个等待
          int newSocketFd = accept(fd,(struct sockaddr*)&clientAddr,&len);
          if(newSocketFd == -1)
          {
               perror("");
               return 0;
          }
          socketFDArr[count] = newSocketFd;//把新连接套接字存储到数组中
          addrArr[count] = clientAddr;//把新客户端的地址保存起来
          //创建一个线程，并且把刚存入数组中的套接字传给线程，让线程使用套接字帮我们接收消息
          pthread_create(&pt,NULL,work,&socketFDArr[count]);
          count++;//连接上的人数+1
          char szIP[16];
          //把accept接收来的地址里面的数字IP转为字符串IP
          numIPtoString(clientAddr.sin_addr.s_addr,szIP,sizeof(szIP));
          printf("接受到新的链接：套接字的是：%d，对方的IP是：%s，对方的端口是：%u\n",newSocketFd,szIP,ntohs(clientAddr.sin_port));
     }

     // while(1)
     // {
     //      char buf[1024];
     //      //从套接字代表网络连接中读取1024个字节到buf中
     //      read(newSocketFd,buf,1024);
     //      printf("收到消息：%s\n",buf);
     // }
     // close(newSocketFd);//关闭通信用的套接字
     close(fd);//关闭接受连接的套接字
     return 0;
}